# -*- coding: utf-8 -*-

import sip
sip.setapi('QString', 2)
sip.setapi('QStringList', 2)
sip.setapi('QVariant', 2)

from PyQt4 import QtCore
from CollectionMetaDataDB import CollectionMetaDataDB

#_______________________________________________________________________________
class CollectionTreeItem(object):
    """FIXME: creare un item per ogni tipo di informazione contenuta"""
    def __init__(self, tagType=None, data=None, tag=None, level=None, parent=None):
        self.parent_item = parent
        self.item_tag = tag
        self.child_items = []
        self.item_tagType = tagType
        self.item_data = data
        self.item_level = level
    
    def child(self, row):
        return self.child_items[row]
    
    def children(self):
        return self.child_items
    
    def childCount(self):
        return len(self.child_items)
    
    def appendChild(self, item):
        self.child_items.append(item)
    
    def popChild(self, row):
        self.child_items.pop(row)
    
    def removeChildren(self, position, count):
        if position < 0 or position + count > len(self.child_items):
            return False
        for row in xrange(count):
            self.child_items.pop(position)
        return True
    
    def appendChildren(self, position, count):
        if position < 0 or position > len(self.child_items):
            return False
        for row in xrange(count):
            item = CollectionTreeItem(parent = self)
            self.child_items.insert(position, item)
        return True
    
    def columnCount(self):
        return 1
    
    def tag(self, column=0):
        return self.item_tag
        
    def setTag(self, value):
        self.item_tag = value
        return True 
    
    def level(self, column=0):
        return self.item_level
    
    def setLevel(self, value):
        self.item_level = value
        return True    

    def data(self, column=0):
        return self.item_data
    
    def setData(self, value):
        self.item_data = value
        return True
    
    def tagType(self, column=0):
        return self.item_tagType
    
    def setTagType(self, value):
        self.item_tagType = value
        return True
    
    def parent(self):
        return self.parent_item
    
    def row(self):
        if self.parent_item:
            return self.parent_item.child_items.index(self)
        return 0
    
    def children_iterator(self):
        list = []
        def visita(item):
            for child in item.children():
                visita(child)
                list.append(child)
        visita(self)
        return iter(list)

#_______________________________________________________________________________
class CollectionTreeModel(QtCore.QAbstractItemModel):
    #FIXME: spostare alcune funzione nel controller
    songs_added_to_playlist = QtCore.pyqtSignal(list)
    
    def __init__(self, parent=None):
        QtCore.QAbstractItemModel.__init__(self, parent)
        self.order_tagTypes = ['artist', 'album']
        self.root_item = CollectionTreeItem()
#        self.server.client_connected.connect(self.connect)
#        self.server.database_updated.connect(self.reload_data)
        self.metadatadb = CollectionMetaDataDB(self)
        self.data_order = "asc"
        
#    def connect(self, songs=None):
#        if connected is True:
#            self.setup_data(songs)
#        else:

    def disconnect(self):
        self.metadatadb.connection.close()
    
    def clear(self):
        self.beginResetModel()
        self.root_item.child_items = []
        self.endResetModel()  
#      
#    def reload_data(self):
#        self.beginResetModel()
#        self.root_item.child_items = []
#        self.endResetModel()
#        self.setup_data(self.root_item)
    
    def columnCount(self, parent):
        return 1
    
    def rowCount(self, parent):
        if not parent.isValid():
            parent_item = self.root_item
        else:
            parent_item = parent.internalPointer()
        return parent_item.childCount()
    
    def remove_children(self, parent):
        if self.hasChildren(parent):
            row = 0
            if not parent.isValid():
                parent_item = self.root_item
            else:
                parent_item = parent.internalPointer()
            count = parent_item.childCount()
            self.removeRows(row, count, parent)
       
    def set_level_1_data(self, parent):
        parent_item = parent.internalPointer()
        parent_tag = parent_item.tag()
        if len(self.order_tagTypes) <= 1:
            tagType = "title"
        else:
            tagType = self.order_tagTypes[1]
        tag_list = self.metadatadb.get_level_1_data(tagType, parent_tag)
        {"Year - Album": self.set_year_album, 
         "album": self.set_album, 
         "date": self.set_year, 
         "artist": self.set_artist, 
         "genre": self.set_genre, 
         "title": self.set_song}[tagType](tag_list, parent)       
    
    def set_level_2_data(self, parent):
        parent_1_item = parent.internalPointer()
        parent_1_tag = parent_1_item.tag()
        parent_0_item = parent_1_item.parent()
        parent_0_tag = parent_0_item.tag()
        tags = (parent_0_tag, parent_1_tag)
        if len(self.order_tagTypes) <= 2:
            tagType = "title"
        else:
            tagType = self.order_tagTypes[2]
        tag_list = self.metadatadb.get_level_2_data(tagType, tags)
        {"Year - Album": self.set_year_album, 
         "album": self.set_album, 
         "date": self.set_year, 
         "artist": self.set_artist, 
         "genre": self.set_genre, 
         "title": self.set_song}[tagType](tag_list, parent)

    def set_level_3_data(self, parent):
        parent_2_item = parent.internalPointer()
        parent_2_tag = parent_2_item.tag()
        parent_1_item = parent_2_item.parent()
        parent_1_tag = parent_1_item.tag()
        parent_0_item = parent_1_item.parent()
        parent_0_tag = parent_0_item.tag()
        tags = (parent_0_tag, parent_1_tag, parent_2_tag)
        tag_list = self.metadatadb.get_level_3_data(tags)
        self.set_song(tag_list, parent)
    
    def set_year_album(self, tag_list, parent):
        pass
        
    def set_song(self, tag_list, parent):
        parent_item = parent.internalPointer()
        level = parent_item.level() + 1
        if not tag_list:
            return
        row = self.rowCount(parent)
        self.beginInsertRows(parent, row, row + len(tag_list) - 1)
        for song in tag_list:
            tag = song[0]
            data = song[0]
            item = CollectionTreeItem("title", data, tag, level, parent_item)
            parent_item.appendChild(item)
        self.endInsertRows()
    
    def set_artist(self, tag_list, parent):
        parent_item = parent.internalPointer()
        level = parent_item.level() + 1
        if not tag_list:
            return
        row = self.rowCount(parent)
        self.beginInsertRows(parent, row, row + len(tag_list) - 1)
        for artist in tag_list:
            tag = artist[0]
            data = artist[0]
            item = CollectionTreeItem("artist", data, tag, level, parent_item)
            parent_item.appendChild(item)
        self.endInsertRows()  
    
    def set_genre(self, tag_list, parent):
        parent_item = parent.internalPointer()
        level = parent_item.level() + 1
        if not tag_list:
            return
        row = self.rowCount(parent)
        self.beginInsertRows(parent, row, row + len(tag_list) - 1)
        for genre in tag_list:
            tag = genre[0]
            data = genre[0]
            item = CollectionTreeItem("genre", data, tag, level, parent_item)
            parent_item.appendChild(item)
        self.endInsertRows()          
    
    def set_album(self, tag_list, parent):
        parent_item = parent.internalPointer()
        level = parent_item.level() + 1
        if not tag_list:
            return
        row = self.rowCount(parent)
        self.beginInsertRows(parent, row, row + len(tag_list) - 1)
        for album in tag_list:
            tag = album[0]
            data = album[0]
            item = CollectionTreeItem("album", data, tag, level, parent_item)
            parent_item.appendChild(item)
        self.endInsertRows() 

    def set_year(self, tag_list, parent):
        parent_item = parent.internalPointer()
        level = parent_item.level() + 1
        if not tag_list:
            return
        row = self.rowCount(parent)
        self.beginInsertRows(parent, row, row + len(tag_list) - 1)
        for year in tag_list:
            tag = year[0]
            data = year[0]
            item = CollectionTreeItem("date", data, tag, level, parent_item)
            parent_item.appendChild(item)
        self.endInsertRows()         
    
    def insertRows(self, row, count, parent=QtCore.QModelIndex()):
        if not parent.isValid():
            parent_item = self.root_item
        else:
            parent_item = parent.internalPointer()
        self.beginInsertRows(parent, row, row + count - 1)
        parent_item.appendChildren(row, count)
        self.endInsertRows()
        return True
    
    def insertRow(self, row, parent=QtCore.QModelIndex()):
        if not parent.isValid():
            parent_item = self.root_item
        else:
            parent_item = parent.internalPointer()
        self.beginInsertRows(parent, row, row)
        parent_item.appendChildren(row, 1)
        self.endInsertRows()
        return True
    
    def removeRows(self, row, count, parent=QtCore.QModelIndex()):
        if not parent.isValid():
            parent_item = self.root_item
        else:
            parent_item = parent.internalPointer()
        self.beginRemoveRows(parent, row, row + count - 1)
        parent_item.removeChildren(row, count)
        self.endRemoveRows()
        return True
    
    def parent(self, child):
        if not child.isValid():
            return QtCore.QModelIndex()
        child_item = child.internalPointer()
        parent_item = child_item.parent()
        if not parent_item or parent_item == self.root_item:
            return QtCore.QModelIndex()
        return self.createIndex(parent_item.row(), 0, parent_item)
    
    def tagType(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return None
        if role != QtCore.Qt.DisplayRole:
            return None
        item = index.internalPointer()
        return item.tagType().decode("utf-8")
    
    def tag(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return
        if role != QtCore.Qt.DisplayRole:
            return
        item = index.internalPointer()
        return item.tag()
    
    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return None
        if role != QtCore.Qt.DisplayRole:
            return None
        item = index.internalPointer()
        if item.data() is None:
            return None
        return item.data()
    
    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if role != QtCore.Qt.EditRole:
            return False
        if not index.isValid():
            return None
        item = index.internalPointer()
        result = item.setData(value)
        if result:
            self.dataChanged.emit(index, index)
        return result
    
    def hasChildren(self, parent):
        if not parent.isValid():
            return True
        item = parent.internalPointer()
        if item.tagType() == "title":
            return False
        return True        
    
    def canFetchMore(self, parent):
        if not parent.isValid():
            return False
        item = parent.internalPointer()
        if item.tagType() == "title":
            return False
        if not item.children() and self.hasChildren(parent):
            return True
        else:
            return False
    
    def fetchMore(self, parent):
        if not parent.isValid():
            return False
        parent_item = parent.internalPointer()
        parent_level = parent_item.level()
        current_level = parent_level + 1
        if current_level == 1:
            self.set_level_1_data(parent)
        elif current_level == 2:
            self.set_level_2_data(parent)
        else:
            self.set_level_3_data(parent)

    def flags(self, index):
        default_flags = QtCore.QAbstractItemModel.flags(self, index)
        if not index.isValid():
            return default_flags
        return QtCore.Qt.ItemIsDragEnabled| QtCore.Qt.ItemIsEnabled | \
                default_flags
    
    def mimeTypes(self):
        types = ["application/x-novocaine-add-songs"]
        return types
        
    def mimeData(self, indexes):
        mime_data = QtCore.QMimeData()
        item_data = QtCore.QByteArray()
        stream_data = QtCore.QDataStream(item_data, QtCore.QIODevice.WriteOnly)
        songs = self.select_songs(indexes)
        if songs:
            for song in songs:
                stream_data.writeString(song)
        mime_data.setData("application/x-novocaine-add-songs", item_data)
        return mime_data    

    def headerData(self, section, orientation, role):
        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
            if self.order_tagTypes:
                header = self.order_tagTypes[0]
            else:
                header = "song"
            for i in xrange(1, len(self.order_tagTypes) - 1):
                header = header + " / " + self.order_tagTypes[i]
            return header
        return
    
    def index(self, row, column, parent):
        if not self.hasIndex(row, column, parent):
            return QtCore.QModelIndex()
        if not parent.isValid():
            parent_item = self.root_item
        else:
            parent_item = parent.internalPointer()
        child_item = parent_item.child(row)
        if child_item:
            return self.createIndex(row, column, child_item)
        else:
            return QtCore.QModelIndex()
    
    def select_songs(self, indexes):
        songs = []
        for index in indexes:
            item = index.internalPointer()
            level = item.level()
            if level == 0:
                tags = [item.tag()]
                tagTypes = [item.tagType()]
                files = self.metadatadb.level_0_files(zip(tagTypes, tags))
            elif level == 1:
                parent_0_item = item.parent()
                tags = [parent_0_item.tag(), item.tag()]
                tagTypes = [parent_0_item.tagType(), item.tagType()]
                files = self.metadatadb.level_1_files(zip(tagTypes, tags))
            elif level == 2:
                parent_1_item = item.parent()
                parent_0_item = parent_1_item.parent()
                tags = [parent_0_item.tag(), parent_1_item.tag(), item.tag()]
                tagTypes = [parent_0_item.tagType(), parent_1_item.tagType(), 
                            item.tagType()]
                files = self.metadatadb.level_2_files(zip(tagTypes, tags))
            else:
                parent_2_item = item.parent()
                parent_1_item = parent_2_item.parent()
                parent_0_item = parent_1_item.parent()
                tags = [parent_0_item.tag(), parent_1_item.tag(), parent_2_item.tag(), 
                        item.tag()]
                tagTypes = [parent_0_item.tagType(), parent_1_item.tagType(), 
                            parent_2_item.tagType(), item.tagType()] 
                files = self.metadatadb.level_3_files(zip(tagTypes, tags))
            songs.extend([file[0] for file in files])
        return songs

    def add_songs_to_playlist(self, indexes):
        songs = self.select_songs(indexes)
        self.songs_added_to_playlist.emit(songs)
    
    def insert_data(self):
        self.clear()
        if self.order_tagTypes:
            tagType = self.order_tagTypes[0]
        else:
            tagType = "title"
        tag_list = self.metadatadb.get_level_0_data(tagType)
        parent = QtCore.QModelIndex()
        self.beginInsertRows(parent, 0, len(tag_list) - 1)
        for tag in tag_list:
            item = CollectionTreeItem(tagType, tag[0], tag[0], 0, self.root_item)
            self.root_item.appendChild(item)
        self.endInsertRows()        
    
    def setup_data(self, songs):
        self.metadatadb.clear_data()
        self.metadatadb.load_data(songs)
        self.insert_data()
